package de.jmda.core.util.jaxb;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.xml.XMLConstants;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.validation.SchemaFactory;

import org.apache.log4j.Logger;

/**
 * TODO add sanity checks (completeness, unmodifiable, ...)
 * TODO add dynamic context configuration ...
 *
 * @author roger@jmda.de
 */
public class ConfigUtil
{
	private final static Logger LOGGER = Logger.getLogger(ConfigUtil.class);

	/**
	 * Information for namespace - schemafile and namespace - prefix mappings.
	 */
	public static class NamespaceInfo
	{
		private final String namespaceURI;
		private final String schemaFilename;
		private final String namespacePrefix;

		public NamespaceInfo(
				String namespaceURI, String schemaFilename, String namespacePrefix)
		{
			super();
			this.namespaceURI = namespaceURI;
			this.schemaFilename = schemaFilename;
			this.namespacePrefix = namespacePrefix;
		}

		public String getNamespaceURI()
		{
			return namespaceURI;
		}

		public String getSchemaFilename()
		{
			return schemaFilename;
		}

		public String getNamespacePrefix()
		{
			return namespacePrefix;
		}
	}

	//	/**
	//	 * Information for namespace - prefix mapper, can be used to specify namespace
	//	 * prefix mapper values for {@link Marshaller#setProperty(String, Object)}.
	//	 */
	//	public static class NamespacePrefixMapperInfo
	//	{
	//		public final static String NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_JDK =
	//				"com.sun.xml.internal.bind.namespacePrefixMapper";
	//		public final static String NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_RI =
	//				"com.sun.xml.bind.namespacePrefixMapper";
	//
	//		private NamespacePrefixMapper namespacePrefixMapper;
	//
	//		/**
	//		 * Initialises {@link #namespacePrefixMapper} with {@link
	//		 * NamespacePrefixMapperInternal} using <code>map</code>.
	//		 *
	//		 * @param map
	//		 */
	//		public NamespacePrefixMapperInfo(Map<String, String> map)
	//		{
	//			namespacePrefixMapper = new NamespacePrefixMapperInternal(map);
	//		}
	//
	//		public NamespacePrefixMapper getNamespacePrefixMapper()
	//		{
	//			return namespacePrefixMapper;
	//		}
	//	}

	//	private static class NamespacePrefixMapperInternal
	//			extends NamespacePrefixMapper
	//	{
	//		private final static Logger LOGGER =
	//        Logger.getLogger(NamespacePrefixMapperInternal.class);
	//
	//		private Map<String, String> namespaceURIs_namespacePrefixes;
	//
	//		private NamespacePrefixMapperInternal(
	//				Map<String, String> namespaceURIs_namespacePrefixes)
	//		{
	//			this.namespaceURIs_namespacePrefixes = namespaceURIs_namespacePrefixes;
	//		}
	//
	//		@Override
	//    public String getPreferredPrefix(
	//    		String namespaceURI, String suggestion, boolean required)
	//    {
	//			String result = namespaceURIs_namespacePrefixes.get(namespaceURI);
	//
	//			LOGGER.debug(
	//					"namespaceURI [" + namespaceURI + "] " +
	//					"suggestion [" + suggestion + "] " +
	//					"required [" + required + "] " +
	//					"returning [" + result + "]");
	//
	//	    return result;
	//    }
	//
	//		@Override
	//		public String[] getPreDeclaredNamespaceUris()
	//	  {
	//	    return namespaceURIs_namespacePrefixes.keySet().toArray(new String[] {});
	//	  }
	//	}

	private final Set<String> namespaceURIs = new HashSet<String>();

	private final Map<String, String> namespaceURIs_schemaFilenames =
			new HashMap<String, String>();

	private final Map<String, String> namespaceURIs_namespacePrefix =
			new HashMap<String, String>();

	public void add(NamespaceInfo metadataItem)
	{
		namespaceURIs.add(metadataItem.namespaceURI);
		namespaceURIs_schemaFilenames.put(metadataItem.namespaceURI, metadataItem.schemaFilename);
		namespaceURIs_namespacePrefix.put(metadataItem.namespaceURI, metadataItem.namespacePrefix);
	}

	public String getSchemaFilenameForNamespaceURI(String namespaceURI)
	{
		return namespaceURIs_schemaFilenames.get(namespaceURI);
	}

	public String getNamespacePrefixForNamespaceURI(String namespaceURI)
	{
		return namespaceURIs_namespacePrefix.get(namespaceURI);
	}

	public String[] getNamespaceURIs()
	{
		return namespaceURIs.toArray(new String[] {});
	}

	public String[] getSchemaFilenames()
	{
		Set<String> result = new HashSet<String>();

		result.addAll(namespaceURIs_schemaFilenames.values());

		return result.toArray(new String[] {});
	}

	//	public NamespacePrefixMapperInfo getNamespacePrefixMapperInfo()
	//	{
	//		return new NamespacePrefixMapperInfo(namespaceURIs_namespacePrefix);
	//	}
	//
	//	public void activateNamespacePrefixMapping(Marshaller marshaller)
	//	{
	//		NamespacePrefixMapperInfo namespacePrefixMapperInfo =
	//				getNamespacePrefixMapperInfo();
	//
	//		try
	//    {
	//	    marshaller.setProperty(
	//	    		NamespacePrefixMapperInfo.NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_JDK,
	//	    		namespacePrefixMapperInfo.getNamespacePrefixMapper());
	//	    return;
	//    }
	//    catch (PropertyException e)
	//    {
	//    	LOGGER.debug(
	//    			"failure setting namespace prefix mapper property using key [" +
	//    			NamespacePrefixMapperInfo.NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_JDK +
	//    			"]", e);
	//    }
	//
	//  	try
	//    {
	//      marshaller.setProperty(
	//      		NamespacePrefixMapperInfo.NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_RI,
	//      		namespacePrefixMapperInfo.getNamespacePrefixMapper());
	//	    return;
	//    }
	//    catch (PropertyException e)
	//    {
	//    	LOGGER.debug(
	//    			"failure setting namespace prefix mapper property using key [" +
	//    			NamespacePrefixMapperInfo.NAMESPACE_PREFIX_MAPPER_KEY_JAXB_IMPL_RI +
	//    			"]", e);
	//    }
	//
	//  	LOGGER.error("failure setting namespace prefix mapper property");
	//	}

	public void activateFormattedOutput(Marshaller marshaller)
	{
		try
		{
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		}
		catch (PropertyException e)
		{
			LOGGER.error(
					"failure setting " + Marshaller.JAXB_FORMATTED_OUTPUT + " property");
		}
	}

	public static SchemaFactory getSchemaFactory()
	{
		return SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
	}
}